import { IdsDto } from '@/common/dto/ids.dto';
import {
  buildRoute,
  buildTree,
  buildTreeOriginal,
  findAncestors,
} from '@/common/utils/tools';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { plainToInstance } from 'class-transformer';
import { Repository } from 'typeorm';
import { CreateMenuDto } from './dto/create-menu.dto';
import { UpdateMenuDto } from './dto/update-menu.dto';
import { SysMenu } from './entities/menu.entity';
import { SearchDto } from '@/common/dto/base-search.dto';
import {
  queryBuilderAndWhereEQ,
  queryBuilderAndWhereLike,
} from '@/common/utils/db.andwhere';

@Injectable()
export class MenusService {
  constructor(
    @InjectRepository(SysMenu)
    private readonly menuRepository: Repository<SysMenu>,
  ) {}
  async create(body: CreateMenuDto) {
    // 尝试将 CreateMenuDto 类型的参数赋值给期望 DeepPartial<SysMenu>[] 类型的参数，但两者类型不兼容。以下是解决方案：
    // 例如：menu实体需要treePath字段，而 CreateMenuDto 并没有定义响应规则，通过plainToInstance进行适配
    const plantBody = plainToInstance(SysMenu, body, {});
    const treePath = await findAncestors<SysMenu>(
      this.menuRepository,
      body.parentId,
    );
    plantBody.treePath = treePath.join(',');
    const temp = this.menuRepository.create({ ...plantBody });
    return this.menuRepository.save(temp);
  }

  async findAllList(query: SearchDto) {
    const menus = await this.findAll(query);
    return buildTreeOriginal(menus);
  }

  findAll(query: SearchDto) {
    let queryBuilder = this.menuRepository
      .createQueryBuilder('menus')
      // .leftJoinAndSelect('menus.sysRoles', 'sysRoles') // 查询菜单时，同时查询菜单对应的角色
      .where('1=1');
    const likeProps = {
      'menus.name': query.keywords,
    };
    queryBuilder = queryBuilderAndWhereLike(queryBuilder, likeProps);
    const eqProps = {
      'menus.status': query.status,
    };
    queryBuilder = queryBuilderAndWhereEQ(queryBuilder, eqProps);
    return queryBuilder.getMany();
  }

  async findOne(id: string) {
    const menu = await this.menuRepository.findOneBy({ id });

    // 通过转换器，将 ResultMenuConverterDto 的规则，将数据转换好返回
    // return plainToInstance(ResultMenuConverterDto, menu, {});
    return menu;
  }

  async update(id: number, body: UpdateMenuDto) {
    const plantBody = plainToInstance(SysMenu, body, {});
    if (plantBody.parentId) {
      const treePath = await findAncestors<SysMenu>(
        this.menuRepository,
        body.parentId,
      );
      plantBody.treePath = treePath.join(',');
    }
    return this.menuRepository.update(id, plantBody);
  }

  remove({ ids }: IdsDto) {
    return this.menuRepository.delete(ids.split(','));
  }

  updateStatus(id: number, query: UpdateMenuDto) {
    return this.menuRepository.update(id, { visible: query.visible });
  }

  async buildRoutesByMenu(roleIds, permButton) {
    let queryBuilder = this.menuRepository
      .createQueryBuilder('menus')
      .leftJoin('menus.sysRoles', 'sysRoles');

    // 查询对应角色的菜单
    queryBuilder = queryBuilder.where('sysRoles.id IN (:...roleIds)', {
      roleIds,
    });

    // 菜单查询 或 按钮查询
    if (permButton) {
      queryBuilder = queryBuilder.andWhere('menus.type = 4');
      return await queryBuilder.getMany();
    } else {
      queryBuilder = queryBuilder.andWhere('menus.type != 4');
      return buildRoute(await queryBuilder.getMany());
    }
  }

  async buildTreeByMenu() {
    let menus = await this.findAll({});
    menus = buildTree(menus);
    return menus;
  }
}
